#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <syslog.h>
#include <sys/stat.h>
#include <errno.h>

#define LOCKFILE "/var/run/daemon.pid"
#define LOCKMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)

// extern int lockfile(int);

/**
 * 对fd指向的文件加写锁
 */
static int lockfile(int fd) {
    struct flock fl;

    fl.l_type = F_WRLCK;
    fl.l_start = 0;
    fl.l_whence = SEEK_SET;
    fl.l_len = 0;

    return (fcntl(fd, F_SETLK, &fl));
}

/**
 * 对fd指向的文件加解锁
 */
static int unlockfile(int fd) {
    struct flock fl;

    fl.l_type = F_UNLCK;
    fl.l_start = 0;
    fl.l_whence = SEEK_SET;
    fl.l_len = 0;

    return (fcntl(fd, F_SETLK, &fl));
}

/**
 * 保证只运行守护进程的一个副本
 * 用一个指定路径的文件, 进行加锁, 只有一个进程持有该锁, 从而确保只有守护进程的一个副本
 */
int already_running(void) {
    int fd;
    char buf[16];

    // 打开指定文件
    fd = open(LOCKFILE, O_RDWR | O_CREAT, LOCKMODE);
    if (fd < 0) {
        syslog(LOG_ERR, "can't open %s: %s", LOCKFILE, strerror(errno));
        exit(1);
    }

    if (lockfile(fd) < 0) { // 文件加锁
        if (errno == EACCES || errno == EAGAIN) {// 别的进程取得锁
            close(fd);
            return 1;
        }
        syslog(LOG_ERR, "can't lock %s: %s", LOCKFILE, strerror(errno));
        exit(1);
    }
    
    ftruncate(fd, 0); // 截断文件fd

    sprintf(buf, "%ld", (long)getpid());
    write(fd, buf, strlen(buf) + 1);

    return 0;
}

